/*
 * OpenTodayConfiguration.java
 *
 * Created on 16  2007, 10:31
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 *
 *Note: This source code is protected under the This software is licensed under the CC-GNU GPL.
 */
package OpenToday.Configuration;

import OpenToday.Tools.ExceptionUtils;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.microedition.io.Connector;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Image;
import javax.microedition.midlet.MIDlet;
import javax.microedition.rms.RecordEnumeration;
import javax.microedition.rms.RecordStore;
import javax.microedition.rms.RecordStoreException;

/**
 *
 * @author:  Menny Even Danan (c) 2007. Note: This source code is protected under the This software is licensed under the CC-GNU GPL.
 */
public class OpenTodayConfiguration {
    //Please note: The string can not be longer than 32 chars!

    private static final String RS_RECORD_I_MAX_EVENTS = "RS_I_MAX_EVENTS";
    private static final String RS_RECORD_I_DAYS_AHEAD = "RS_I_DAYS_AHEAD";
    private static final String RS_RECORD_I_TOP_OFFSET = "RS_I_TOP_OFFSET";
    private static final String RS_RECORD_L_REFRESH_INTERVAL = "RS_L_REFRESH_INTERVAL";
    private static final String RS_RECORD_B_SHOW_EVENTS_FROM_START_OF_DAY = "RS_B_EVENT_FROM_START_OF_DAY";
    private static final String RS_RECORD_B_SHOW_PRIVATE_EVENTS = "RS_B_SHOW_PRIVATE_EVENTS";
    private static final String RS_RECORD_S_NO_EVENTS_TEXT = "RS_S_NO_EVENTS_TEXT";
    private static final String RS_RECORD_B_TEXT_BASED_TODAY_SCREEN = "RS_B_TEXT_BASED_SCREEN";
    private static final String RS_RECORD_B_UTC_EVENTS = "RS_B_UTC_EVENTS";
    private static final String RS_RECORD_S_BACKGROUD_IMAGE = "RS_S_BACKGROUD_IMAGE";
    private static final String RS_RECORD_UseThemeColors = "RS_RECORD_UseThemeColors";
    private static final String RS_RECORD_IA_BACKGROUD_RGB = "RS_IA_BACKGROUD_RGB";
    private static final String RS_RECORD_IA_DAY_TEXT_RGB = "RS_IA_DAY_TEXT_RGB";
    private static final String RS_RECORD_IA_EVENT_TEXT_RGB = "RS_IA_EVENT_TEXT_RGB";
    private static final String RS_RECORD_B_SHOW_OVERDUE_TASK = "RS_B_SHOW_OVERDUE";
    private static final String RS_RECORD_S_OVERDUE_TEXT = "RS_S_OVERDUE_TEXT";
    private static final String RS_RECORD_B_SHOW_DONE_TASKS = "RS_B_SHOW_DONE";
    private static final String RS_RECORD_B_SHOW_TODAY_ALWAYS = "RS_B_SHOW_TODAY_ALWAYS";
    private static final String RS_RECORD_B_DISCARD_ERRORS = "RS_B_DISCARD_ERRORS";
    private static final String RS_RECORD_I_TITLE_FONT_SIZE = "RS_I_TITLE_FONT_SIZE";
    private static final String RS_RECORD_I_EVENT_FONT_SIZE = "RS_I_EVENT_FONT_SIZE";
    private static final String RS_RECORD_B_SHOW_END_TIME = "RS_B_SHOW_END_TIME";
    private static OpenTodayConfiguration ms_instance;

    public synchronized static OpenTodayConfiguration getInstance() {
        if (ms_instance == null) {
            ms_instance = new OpenTodayConfiguration();
        }

        return ms_instance;
    }
    private MIDlet m_currentMidlet;
    private int m_maxEvents;
    private int m_daysAhead;
    private int m_screenTopOffset;
    private long m_refreshIntervalInMS;
    private boolean m_showEventsFromStartOfToday;
    private boolean m_showPrivateEvents;
    private boolean m_TextBasedTodayScreen;
    private String m_noEventsText;
    private boolean m_UTC_events;
    private String m_backgroundImagePath;
    private ImageLoaderThread m_imageLoader;
    private boolean m_UseThemeColors;
    private int[] m_backgroundRGB;
    private int[] m_dayTextRGB;
    private int[] m_eventTextRGB;
    private boolean m_showOverdueTasks;
    private String m_overdueTasksText;
    private boolean m_showDoneTasks;
    private boolean m_showTodayAlways;
    private boolean m_discardErrors;
    private int m_eventFontSize;
    private int m_titleFontSize;
    private boolean m_showEndTime;

    /**
     * Creates a new instance of OpenTodayConfiguration
     */
    private OpenTodayConfiguration() {
        ExceptionUtils.PHASE("start OpenTodayConfiguration");
        m_maxEvents = 5;
        m_daysAhead = 6;
        m_screenTopOffset = 28;
        m_refreshIntervalInMS = 2 * 60 * 1000;//2 minutes
        m_showEventsFromStartOfToday = false;
        m_showPrivateEvents = true;
        m_noEventsText = "Nothing";
        m_TextBasedTodayScreen = false;
        m_UTC_events = true;
        m_backgroundImagePath = "";
        m_UseThemeColors = false;
        m_backgroundRGB = new int[]{0, 0, 70};
        m_dayTextRGB = new int[]{204, 204, 255};
        m_eventTextRGB = new int[]{204, 204, 255};
        m_showOverdueTasks = true;
        m_overdueTasksText = "Overdue";
        m_showDoneTasks = false;
        m_showTodayAlways = true;
        m_discardErrors = true;
        m_eventFontSize = Font.SIZE_SMALL;
        m_titleFontSize = Font.SIZE_SMALL;
        m_showEndTime = false;

        ExceptionUtils.PHASE("end OpenTodayConfiguration");
        try {
            //do not create if not exist. We'll create it at shutdown.
            m_maxEvents = GetRecordInt(RS_RECORD_I_MAX_EVENTS);
            m_daysAhead = GetRecordInt(RS_RECORD_I_DAYS_AHEAD);
            m_screenTopOffset = GetRecordInt(RS_RECORD_I_TOP_OFFSET);
            m_refreshIntervalInMS = GetRecordLong(RS_RECORD_L_REFRESH_INTERVAL);
            m_showEventsFromStartOfToday = GetRecordBoolean(RS_RECORD_B_SHOW_EVENTS_FROM_START_OF_DAY);
            m_showPrivateEvents = GetRecordBoolean(RS_RECORD_B_SHOW_PRIVATE_EVENTS);
            m_noEventsText = GetRecordString(RS_RECORD_S_NO_EVENTS_TEXT);
            m_TextBasedTodayScreen = GetRecordBoolean(RS_RECORD_B_TEXT_BASED_TODAY_SCREEN);
            m_UTC_events = GetRecordBoolean(RS_RECORD_B_UTC_EVENTS);
            m_backgroundImagePath = GetRecordString(RS_RECORD_S_BACKGROUD_IMAGE);
            m_UseThemeColors = GetRecordBoolean(RS_RECORD_UseThemeColors);
            m_backgroundRGB = GetRecordIntArray(RS_RECORD_IA_BACKGROUD_RGB);
            m_dayTextRGB = GetRecordIntArray(RS_RECORD_IA_DAY_TEXT_RGB);
            m_eventTextRGB = GetRecordIntArray(RS_RECORD_IA_EVENT_TEXT_RGB);
            m_showOverdueTasks = GetRecordBoolean(RS_RECORD_B_SHOW_OVERDUE_TASK);
            m_overdueTasksText = GetRecordString(RS_RECORD_S_OVERDUE_TEXT);
            m_showDoneTasks = GetRecordBoolean(RS_RECORD_B_SHOW_DONE_TASKS);
            m_showTodayAlways = GetRecordBoolean(RS_RECORD_B_SHOW_TODAY_ALWAYS);
            m_discardErrors = GetRecordBoolean(RS_RECORD_B_DISCARD_ERRORS);
            m_eventFontSize = GetRecordInt(RS_RECORD_I_EVENT_FONT_SIZE);
            m_titleFontSize = GetRecordInt(RS_RECORD_I_TITLE_FONT_SIZE);
            m_showEndTime = GetRecordBoolean(RS_RECORD_B_SHOW_END_TIME);
        } catch (Exception ex) {
            ExceptionUtils.PHASE("RecordStore error " + ex.getMessage());
            System.out.println("Exception while reading configuration:" + ex.getMessage());
            ex.printStackTrace();
        }
        m_imageLoader = new ImageLoaderThread(m_backgroundImagePath);
        ExceptionUtils.PHASE("end OpenTodayConfiguration");
    }

    public void InitializeOpenTodayConfiguration(MIDlet midlet) {
        m_currentMidlet = midlet;
    }

    public void Shutdown() {
        System.out.println("OpenTodayConfiguration.SaveConfiguration()");
        try {
            //do not create if not exist. We'll create it at shutdown.
            SaveRecord(RS_RECORD_I_MAX_EVENTS, m_maxEvents);
            SaveRecord(RS_RECORD_I_DAYS_AHEAD, m_daysAhead);
            SaveRecord(RS_RECORD_I_TOP_OFFSET, m_screenTopOffset);
            SaveRecord(RS_RECORD_L_REFRESH_INTERVAL, m_refreshIntervalInMS);
            SaveRecord(RS_RECORD_B_SHOW_EVENTS_FROM_START_OF_DAY, m_showEventsFromStartOfToday);
            SaveRecord(RS_RECORD_B_SHOW_PRIVATE_EVENTS, m_showPrivateEvents);
            SaveRecord(RS_RECORD_S_NO_EVENTS_TEXT, m_noEventsText);
            SaveRecord(RS_RECORD_B_TEXT_BASED_TODAY_SCREEN, m_TextBasedTodayScreen);
            SaveRecord(RS_RECORD_B_UTC_EVENTS, m_UTC_events);
            SaveRecord(RS_RECORD_S_BACKGROUD_IMAGE, m_backgroundImagePath);
            SaveRecord(RS_RECORD_UseThemeColors, m_UseThemeColors);
            SaveRecord(RS_RECORD_IA_BACKGROUD_RGB, m_backgroundRGB);
            SaveRecord(RS_RECORD_IA_DAY_TEXT_RGB, m_dayTextRGB);
            SaveRecord(RS_RECORD_IA_EVENT_TEXT_RGB, m_eventTextRGB);
            SaveRecord(RS_RECORD_B_SHOW_OVERDUE_TASK, m_showOverdueTasks);
            SaveRecord(RS_RECORD_S_OVERDUE_TEXT, m_overdueTasksText);
            SaveRecord(RS_RECORD_B_SHOW_DONE_TASKS, m_showDoneTasks);
            SaveRecord(RS_RECORD_B_SHOW_TODAY_ALWAYS, m_showTodayAlways);
            SaveRecord(RS_RECORD_B_DISCARD_ERRORS, m_discardErrors);
            SaveRecord(RS_RECORD_I_EVENT_FONT_SIZE, m_eventFontSize);
            SaveRecord(RS_RECORD_I_TITLE_FONT_SIZE, m_titleFontSize);
            SaveRecord(RS_RECORD_B_SHOW_END_TIME, m_showEndTime);
        } catch (Exception ex) {
            System.out.println("Exception while reading configuration:" + ex.getMessage());
            ex.printStackTrace();
        }
        m_imageLoader.Shutdown();
    }

    public Display get_Display() {
        return Display.getDisplay(m_currentMidlet);
    }

    public boolean get_ShowPrivateEvents() {
        return m_showPrivateEvents;
    }

    public boolean get_ShowEventsFromStartOfToday() {
        return m_showEventsFromStartOfToday;
    }

    public int get_MaxEventsToShow() {
        return m_maxEvents;
    }

    public void set_MaxEventsToShow(int value) {
        m_maxEvents = value;
    }

    public int get_DaysAheadToLook() {
        return m_daysAhead;
    }

    public void set_DaysAheadToLook(int value) {
        m_daysAhead = value;
    }

    public int get_ScreenTopOffset() {
        return m_screenTopOffset;
    }

    public void set_ScreenTopOffset(int value) {
        m_screenTopOffset = value;
    }

    public long get_RefreshEventsListInMilliseconds() {
        return m_refreshIntervalInMS;
    }

    public void set_RefreshEventsListInMilliseconds(long value) {
        m_refreshIntervalInMS = value;
    }

    public String get_NoEventsText() {
        return m_noEventsText;
    }

    public void set_NoEventsText(String newText) {
        m_noEventsText = newText;
    }

    public String get_BackgroundImagePath() {
        return m_backgroundImagePath;
    }

    public void set_BackgroundImagePath(String fullPath) {
        m_backgroundImagePath = fullPath;
        m_imageLoader.LoadAnImage(m_backgroundImagePath);
    }

    public Image GetLoadedBackgroundImage() {
        return m_imageLoader.GetLoadedImage();
    }

    public boolean get_TextBasedTodayScreen() {
        return m_TextBasedTodayScreen;
    }

    public void set_TextBasedTodayScreen(boolean val) {
        m_TextBasedTodayScreen = val;
    }

    public boolean get_EventsStoredInUTC() {
        return m_UTC_events;
    }

    public void set_EventsStoredInUTC(boolean val) {
        m_UTC_events = val;
    }

    /**
     * set / get m_UseThemeColors
     **/
    public boolean get_UseThemeColors() {
        return m_UseThemeColors;
    }

    /**
     * set / get m_UseThemeColors
     **/
    public void set_UseThemeColors(boolean val) {
        m_UseThemeColors = val;
    }

    /**
     * determine system color from MIDlet
     * @return system color as int
     **/
    public int getSystemBackgroundColor() {

        Display display = get_Display();
        return (display.getColor(Display.COLOR_BACKGROUND));
    }

    /**
     * Used externally to determine correct color
     * returns either m_backgroundRGB or SystemBackgroundColor depending on m_UseThemeColors
     **/
    public int[] get_DisplayBackgroundRGB() {
        if (m_UseThemeColors) {
            return ico24ToRGB(getSystemBackgroundColor());
        } else {
            return m_backgroundRGB;
        }
    }

    /**
     * Use internally to determine correct color, for save, store
     **/
    public int[] get_BackgroundRGB() {

        return m_backgroundRGB;
    }

    public void set_BackgroundRGB(int[] val) {
        m_backgroundRGB = val;
    }

    /**
     * Used externally to determine correct color
     * returns either m_dayTextRGB or SystemForegroundColor depending on m_UseThemeColors
     **/
    public int[] get_DisplayDayTextRGB() {
        if (m_UseThemeColors) {
            return ico24ToRGB(getSystemForegroundColor());
        } else {
            return m_dayTextRGB;
        }
    }

    public int[] get_DayTextRGB() {
        return m_dayTextRGB;
    }

    public void set_DayTextRGB(int[] val) {
        m_dayTextRGB = val;
    }

    public int getSystemForegroundColor() {

        Display display = get_Display();
        return (display.getColor(Display.COLOR_FOREGROUND));
    }

    /**
     * Used externally to determine correct color
     * returns either m_eventTextRGB or SystemForegroundColor depending on m_UseThemeColors
     **/
    public int[] get_DisplayEventTextRGB() {
        if (m_UseThemeColors) {
            return ico24ToRGB(getSystemForegroundColor());
        } else {
            return m_eventTextRGB;
        }
    }

    public int[] get_EventTextRGB() {

        return m_eventTextRGB;
    }

    public void set_EventTextRGB(int[] val) {
        m_eventTextRGB = val;
    }

    public boolean getShowOverdueItems() {
        return m_showOverdueTasks;
    }

    public void setShowOverdueItems(boolean newValue) {
        m_showOverdueTasks = newValue;
    }

    public String getOverdueItemsTitle() {
        return m_overdueTasksText;
    }

    public void setOverdueItemsTitle(String newValue) {
        m_overdueTasksText = newValue;
    }

    public boolean getShowDoneTasks() {
        return m_showDoneTasks;
    }

    public void setShowDoneTasks(boolean newValue) {
        m_showDoneTasks = newValue;
    }

    public boolean getShowTodayAlways() {
        return m_showTodayAlways;
    }

    public void setShowTodayAlways(boolean newValue) {
        m_showTodayAlways = newValue;
    }

    public boolean getDiscardErrors() {
        return m_discardErrors;
    }

    public void setDiscardErrors(boolean newValue) {
        m_discardErrors = newValue;
    }

    public boolean getShowEndTime() {
        return m_showEndTime;
    }

    public void setShowEndTime(boolean newValue) {
        m_showEndTime = newValue;
    }

    public int getEventFontSize() {
        return m_eventFontSize;
    }

    public void setEventFontSize(int size) {
        m_eventFontSize = size;
    }

    public int getTitleFontSize() {
        return m_titleFontSize;
    }

    public void setTitleFontSize(int size) {
        m_titleFontSize = size;
    }

    private static byte[] CreateBytes(String text) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        DataOutputStream outputStream = new DataOutputStream(baos);
        outputStream.writeUTF(text);
        return baos.toByteArray();
    }

    private void SaveRecord(String rs_name, int val) throws IOException, RecordStoreException {
        SaveRecord(rs_name, "" + val);
    }

    private void SaveRecord(String rs_name, long val) throws IOException, RecordStoreException {
        SaveRecord(rs_name, "" + val);
    }

    private void SaveRecord(String rs_name, int[] valAI) throws IOException, RecordStoreException {
        SaveRecord(rs_name, GetRGBString(valAI));
    }

    private void SaveRecord(String rs_name, String val) throws IOException, RecordStoreException {
        try {
            RecordStore rs = RecordStore.openRecordStore(rs_name, true, RecordStore.AUTHMODE_PRIVATE, true);
            RecordEnumeration delE = rs.enumerateRecords(null, null, false);
            while (delE.hasNextElement()) {
                rs.deleteRecord(delE.nextRecordId());
            }
            delE.destroy();
            rs.closeRecordStore();
        } catch (Exception ex) {
            System.err.println("Exception while deleting record " + rs_name + ". Error: " + ex.getMessage());
            ex.printStackTrace();
        }

        try {
            byte[] bytes = val.getBytes();
            final int STEP_SIZE = 16;
            RecordStore rs = RecordStore.openRecordStore(rs_name, true, RecordStore.AUTHMODE_PRIVATE, true);
            for (int i = 0; i < bytes.length; i += STEP_SIZE) {
                int length = bytes.length - i;
                if (length > STEP_SIZE) {
                    length = STEP_SIZE;
                }
                rs.addRecord(bytes, i, length);
            }
        } catch (Exception e) {
        }
    }

    private void SaveRecord(String rs_name, boolean val) throws IOException, RecordStoreException {
        SaveRecord(rs_name, val ? "Y" : "N");
    }

    private boolean GetRecordBoolean(String rs_name) throws Exception {

        String boolVal = GetRecordString(rs_name);
        return boolVal.equalsIgnoreCase("Y");
    }

    private int GetRecordInt(String rs_name) throws Exception {
        String intVal = GetRecordString(rs_name);
        return Integer.parseInt(intVal);
    }

    public static int[] GetRGBValues(String RGB_csv) {
        int[] RGB = new int[3];
        int lastComma = 0;
        try {
            for (int i = 0; i < 3; i++) {
                int nextComma = RGB_csv.indexOf(",", lastComma);
                if (nextComma == -1)//no next
                {
                    nextComma = RGB_csv.length();//till the end of the string
                }
                String color = RGB_csv.substring(lastComma, nextComma);
                RGB[i] = Integer.parseInt(color);
                lastComma = nextComma + 1;
            }
        } catch (Exception ex) {
            System.err.println("Error while parsing RGB! " + ex.getMessage());
            ex.printStackTrace();
        }

        return RGB;
    }

    public static String GetRGBString(int[] RGB) {
        return RGB[0] + "," + RGB[1] + "," + RGB[2];
    }

    private int[] GetRecordIntArray(String rs_name) throws Exception {
        String intVal = GetRecordString(rs_name);
        return GetRGBValues(intVal);
    }

    /**
     * Converts a CharacterProperties.getIco24() value  into rgb format.
     * Ico24 (or ColorRef) values, have the hexadecimal form: 0x00BBGGRR.
     * (Applies to binary file formats only)
     *
     * source: http://cfsearching.blogspot.com/2009/05/java-poi-hwpf-characterproperties.html
     *
     * @param ico24 - Word colorRef value returned by CharacterProperties.getIco24()
     * @return Array of color values [0] red, [1] green, [2] blue
     */
    public static int[] ico24ToRGB(int ico24) {
        int[] rgb = new int[3];
        
        rgb[2]   = (ico24 >> 0) & 0xff;  // red;
        rgb[1]   = (ico24 >> 8) & 0xff;  // green
        rgb[0]   = (ico24 >> 16) & 0xff;  // blue
        int alfa = (ico24 >> 24) & 0xff;

        return rgb;
    }


    private long GetRecordLong(String rs_name) throws Exception {
        String longVal = GetRecordString(rs_name);
        return Long.parseLong(longVal);
    }

    private String GetRecordString(String rs_name) throws Exception {
        RecordStore rs = RecordStore.openRecordStore(rs_name, false, RecordStore.AUTHMODE_PRIVATE, false);

        String val = "";

        RecordEnumeration e = null;
        try {
            e = rs.enumerateRecords(null, null, false);
            while (e.hasNextElement()) {
                byte[] bytes = e.nextRecord();
                //reads in the oppisite direction?
                val = (new String(bytes)).concat(val);
            }
            return val;
        } finally {
            if (e != null) {
                e.destroy();
            }
        }
    }

    private class ImageLoaderThread implements Runnable {

        private final Object m_monitor;
        private final Object m_loadWaiter;
        private String m_backgroundImagePath;
        private Image m_loadedBackgroundImage;
        private Thread m_thread;
        private boolean m_running;

        public ImageLoaderThread(String imagePath) {
            m_monitor = new Object();
            m_loadWaiter = new Object();
            m_backgroundImagePath = imagePath;
            m_loadedBackgroundImage = null;
            m_running = true;
            m_thread = new Thread(this, "ImageLoaderThread");
            m_thread.start();
        }

        public void run() {
            String lastLoadedImage = "";
            while (m_running) {
                //System.out.println("Loader thread b4 lock");
                synchronized (m_monitor) {
                    if (!lastLoadedImage.equals(m_backgroundImagePath)) {
                        try {
                            String imageToLoad = "file:///" + m_backgroundImagePath;
                            //System.out.println("About to load background image: " + imageToLoad);
                            InputStream in = null;
                            ByteArrayOutputStream bis = null;
                            try {
                                in = Connector.openInputStream(imageToLoad);
                                bis = new ByteArrayOutputStream();
                                int aReadByte = 0;
                                while ((aReadByte = in.read()) != -1) {
                                    System.out.print("r");
                                    if ((bis.size() % 512) == 0) {
                                        System.out.println("r");
                                    }
                                    bis.write(aReadByte);
                                }

                                byte[] imageData = bis.toByteArray();
                                if (imageData.length > 0) {
                                    System.out.println("Image loaded. Size " + imageData.length);
                                    in.read(imageData, 0, imageData.length);
                                    System.out.println("Image data retreived. Creating image.");
                                    m_loadedBackgroundImage = Image.createImage(imageData, 0, imageData.length);
                                    //since we got here, it means that we loaded the image. Saving the name.
                                    lastLoadedImage = m_backgroundImagePath;
                                } else {
                                    System.out.println("Image was not loaded. Size is zero!");
                                    m_loadedBackgroundImage = null;
                                    lastLoadedImage = "";//maybe next run will be better.
                                }
                            } finally {
                                if (in != null) {
                                    try {
                                        in.close();/*waiting till something happen*/
                                    } catch (Exception ex) {
                                        ex.printStackTrace();
                                    }
                                }
                            }
                        } catch (Exception ex) {
                            ex.printStackTrace();
                            m_loadedBackgroundImage = null;
                            lastLoadedImage = "";//maybe next run will be better.
                        }
                    }
                }
                System.out.println("ImageLoader: Wait for something to happen...");
                synchronized (m_loadWaiter) {
                    try {
                        m_loadWaiter.wait();
                    } catch (Exception ex) {
                    }
                }
                System.out.println("ImageLoader: Something happend!");

            }
            System.out.println("ImageLoader: End of thread!");
        }

        public void LoadAnImage(String path) {
            System.out.println("LoadAnImage (b4 lock): " + path);
            synchronized (m_monitor) {
                System.out.println("LoadAnImage (after lock): " + path);
                m_backgroundImagePath = path;
            }
            synchronized (m_loadWaiter) {
                m_loadWaiter.notifyAll();
            }
        }

        public void Shutdown() {
            synchronized (m_monitor) {
                m_running = false;
                m_monitor.notifyAll();
            }
        }

        private Image GetLoadedImage() {
            System.out.println("GetLoadedImage b4 lock");
            synchronized (m_monitor) {
                System.out.println("GetLoadedImage after lock");
                return m_loadedBackgroundImage;
            }
        }
    }
}
